#ifndef __MY_MATCH_H__
#define __MY_MATCH_H__
#include "Util.hpp"
#include <list>
#include <condition_variable>
#include "Room.hpp"
#include "OnlineManager.hpp"
#include "Database.hpp"
#include <algorithm>

// 匹配队列
template <typename T>
class MatchQueue
{
public:
    size_t Size()
    {
        std::unique_lock<std::mutex> lockguard(_mtx);
        return _list.size();
    }
    bool Empty()
    {
        std::unique_lock<std::mutex> lockguard(_mtx);
        return _list.empty();
    }
    void Push(const T &data)
    {
        std::unique_lock<std::mutex> lockguard(_mtx);
        _list.emplace_back(data);
        if (_list.size() >= 2)
        {
            _cond.notify_all();
        }
    }
    void Pop(T &data1, T &data2)
    {
        std::unique_lock<std::mutex> lockguard(_mtx);
        while (_list.size() < 2)
        {
            _cond.wait(lockguard);
        }
        data1 = _list.front();
        _list.pop_front();
        data2 = _list.front();
        _list.pop_front();
    }
    void Remove(const T &data)
    {
        std::unique_lock<std::mutex> lockguard(_mtx);
        _list.remove(data);
    }

private:
    std::mutex _mtx;
    std::list<T> _list; // 不直接使用queue，因为涉及到对队列中间元素的删除
    // 一个线程阻塞等待
    // 另一个线程向MatchQueue中放入数据，当元素个数>=2时唤醒第一个线程取出数据完成匹配
    std::condition_variable _cond; // 用于阻塞等待队列中的元素个数>=2
};

template <size_t N>
class Matcher
{
private:
    void ThreadEntry(uint32_t i)
    {
        while (true)
        {
            // 从匹配队列取出两个玩家
            id_t player1, player2;
            _queues[i].Pop(player1, player2);

            // 检查玩家是否掉线
            ws_server_t::connection_ptr conn1 = _om->GetConFromHall(player1);
            if (conn1.get() == nullptr)
            {
                Add(player2);
                continue;
            }
            ws_server_t::connection_ptr conn2 = _om->GetConFromHall(player2);
            if (conn2.get() == nullptr)
            {
                Add(player1);
                continue;
            }

            // 为两个玩家创建游戏房间
            RoomPtr room = _rm->CreateRoom(player1, player2);
            // LOG(LogLevel::DEBUG) << "为两位玩家创建房间: " << player1 << " " << player2;
            if (room.get() == nullptr)
            {
                Add(player1);
                Add(player2);
                continue;
            }

            // 匹配成功，回复玩家
            Json::Value resp = Response::MatchSuccess();
            std::string body;
            Util_Json::Serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
    }

public:
    Matcher(RoomManager *rm, OnlineManager *om, Table_User *tb)
        : _queues(N), _rm(rm), _om(om), _tb(tb)
    {
        _threads.reserve(N);
        for (int i = 0; i < N; i++)
        {
            _threads.push_back(std::thread(&Matcher::ThreadEntry, this, i));
        }
        LOG(LogLevel::INFO) << "匹配管理模块创建成功";
    }
    bool Add(id_t uid)
    {
        Json::Value user;
        bool ret = _tb->SelectById(uid, user);
        if (!ret)
        {
            LOG(LogLevel::ERROR) << "玩家[id=" << uid << "]信息查询失败";
            return false;
        }

        uint32_t points = std::max(base_points, user["points"].asUInt());
        uint32_t pos = std::min(N - 1, (size_t)(points - base_points) / gap_points);
        _queues[pos].Push(uid);
        return true;
    }
    bool Delete(id_t uid)
    {
        Json::Value user;
        bool ret = _tb->SelectById(uid, user);
        if (!ret)
        {
            LOG(LogLevel::ERROR) << "玩家[id=" << uid << "]信息查询失败";
            return false;
        }

        // 计算玩家属于哪一个匹配队列
        uint32_t points = std::max(base_points, user["points"].asUInt());
        uint32_t pos = std::min(N - 1, (size_t)(points - base_points) / gap_points);
        _queues[pos].Remove(uid);
        return true;
    }
    ~Matcher()
    {
        for (auto &thread : _threads)
        {
            thread.join();
        }
        LOG(LogLevel::INFO) << "匹配管理模块即将销毁";
    }

private:
    // 不同段位的匹配队列
    std::vector<MatchQueue<id_t>> _queues;
    // 处理不同段位匹配的线程
    std::vector<std::thread> _threads;
    // 基准分数
    static const uint32_t base_points = 1200;
    // 段位间隔分数
    static const uint32_t gap_points = 600;
    RoomManager *_rm;
    OnlineManager *_om;
    Table_User *_tb;
};

template <size_t N>
const uint32_t Matcher<N>::base_points;

template <size_t N>
const uint32_t Matcher<N>::gap_points;

#endif